#include "doorbell_mqtt.h"
#include "mqtt_client.h"
#include <string.h>
#include <stdlib.h>
#include <esp_log.h>
#include <cJSON.h>

#define TAG "doorbell_mqtt"
#define DOORBELL_MQTT_MAX_CALLBACKS 10

static esp_mqtt_client_handle_t client = NULL;
static doorbell_mqtt_cmd_cb *callbacks[DOORBELL_MQTT_MAX_CALLBACKS] = {0};

static void doorbell_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{

    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id)
    {
    case MQTT_EVENT_CONNECTED:
        // 连接上服务器后，订阅主题
        msg_id = esp_mqtt_client_subscribe_single(client, MQTT_PULL_TOPIC, 0);
        if (msg_id < 0)
        {
            ESP_LOGE(TAG, "Mqtt client subscribe failed, rc=%d", msg_id);
        }
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(TAG, "Mqtt client subscribed");
        doorbell_mqtt_publish("{\"status\":\"ready\"}", 18);
        break;

    case MQTT_EVENT_DATA:
        // 处理收到的数据
        // 使用cJSON解析
        cJSON *root = cJSON_ParseWithLength(event->data, event->data_len);
        if (!root)
        {
            ESP_LOGE(TAG, "Mqtt client parse json failed");
            break;
        }
        cJSON *cmd_json = cJSON_GetObjectItem(root, "cmd");
        if (!cJSON_IsString(cmd_json))
        {
            ESP_LOGE(TAG, "Message format error");
            cJSON_Delete(root);
            break;
        }
        char *cmd = cmd_json->valuestring;
        // 寻找对应指令
        size_t i = 0;
        for (i = 0; callbacks[i]; i++)
        {
            if (strcmp(callbacks[i]->cmd, cmd) == 0)
            {
                // 找到对应指令
                ESP_LOGI(TAG, "Find command %s", cmd);
                callbacks[i]->cb(root);
                break;
            }
        }
        if (!callbacks[i])
        {
            ESP_LOGE(TAG, "Unknown command");
        }

        cJSON_Delete(root);
        break;

    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
}

void doorbell_mqtt_init(void)
{
    memset(callbacks, 0, 10 * sizeof(doorbell_mqtt_cmd_cb *));
    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.uri = MQTT_SERVER,
        .session.keepalive = 25,
        .credentials.client_id = MQTT_CLIENT_ID,
    };
    client = esp_mqtt_client_init(&mqtt_cfg);
    assert(client);
    ESP_ERROR_CHECK(esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, doorbell_mqtt_event_handler, NULL));
    ESP_ERROR_CHECK(esp_mqtt_client_start(client));
}

void doorbell_mqtt_publish(const char *message, int len)
{
    esp_mqtt_client_publish(client, MQTT_PUSH_TOPIC, message, len, 0, 0);
}

void doorbell_mqtt_register_callback(doorbell_mqtt_cmd_cb *cb)
{
    size_t i = 0;
    for (i = 0; i < DOORBELL_MQTT_MAX_CALLBACKS; i++)
    {
        // 2. 没有注册过，但是有空位：注册
        if (!callbacks[i])
        {
            // 当前索引为空位
            callbacks[i] = malloc(sizeof(doorbell_mqtt_cmd_cb));
            assert(callbacks[i]);
            break;
        }
        // 1. 有可能已经注册过：覆盖
        if (strcmp(callbacks[i]->cmd, cb->cmd) == 0)
        {
            // 找到已经注册的相同指令
            break;
        }
    }
    // 3. 没有注册，也没有空位：报错
    if (i >= DOORBELL_MQTT_MAX_CALLBACKS)
    {
        // 没找到空位
        ESP_LOGW(TAG, "No empty slot for callback");
        return;
    }

    memcpy(callbacks[i], cb, sizeof(doorbell_mqtt_cmd_cb));
}

void doorbell_mqtt_deinit()
{
    ESP_ERROR_CHECK(esp_mqtt_client_disconnect(client));
    ESP_ERROR_CHECK(esp_mqtt_client_destroy(client));
}
